
#include <scene_editor/scenetreemodel.h>
#include <session.h>
#include <scene.h>

int ObjectOverviewTreeModel::Item::IndexOf(Item *item) const
{
  for (int i=0, j=Children.size(); i<j; ++i)
    {
      if (Children[i] == item)
        {
          return i;
        }
    }

  return -1;
}

ObjectOverviewTreeModel::ObjectOverviewTreeModel()
  : _root(0)
  , _showCompositions (true)
{
  Clear ();
}

void ObjectOverviewTreeModel::SetShowComposition(bool showComposition)
{
  _showCompositions = showComposition;
}

bool ObjectOverviewTreeModel::IsShowComposition() const
{
  return _showCompositions;
}

bool ObjectOverviewTreeModel::IsLODNode(const QModelIndex &index) const
{
  if (!index.isValid())
    {
      return false;
    }

  Item* item = static_cast<Item*>(index.internalPointer());
  if (!item)
    {
      return false;
    }

  return item->type == Item::Type_LODNode;
}

ceLODNode* ObjectOverviewTreeModel::AsLODNode(const QModelIndex &index) const
{
  if (!index.isValid())
    {
      return false;
    }

  Item* item = static_cast<Item*>(index.internalPointer());
  if (!item)
    {
      return false;
    }

  if (item->type != Item::Type_LODNode)
    {
      return false;
    }

  return item->lodNode;
}

ObjectOverviewTreeModel::Item* ObjectOverviewTreeModel::FindItem(Item *item, Item::Type type)
{
  if (item && item->type == type)
    {
      return item;
    }

  foreach (Item* child, item->Children)
    {
      Item* itm = FindItem (child, type);
      if (itm)
        {
          return itm;
        }
    }
  return 0;

}

ObjectOverviewTreeModel::Item* ObjectOverviewTreeModel::FindItem(Item *item, ceNode *node)
{
  if (item && item->Node == node)
    {
      return item;
    }

  foreach (Item* child, item->Children)
    {
      Item* itm = FindItem (child, node);
      if (itm)
        {
          return itm;
        }
    }
  return 0;
}

ObjectOverviewTreeModel::Item* ObjectOverviewTreeModel::GetItem(ceNode *node)
{
  return FindItem (_root, node);
}


ObjectOverviewTreeModel::Item* ObjectOverviewTreeModel::GetGeometryItem(ceNode *node)
{
  Item* item = FindItem (_root, node);
  if (!item)
    {
      return 0;
    }

  return FindItem (item, Item::Type_Geometry);
}

ObjectOverviewTreeModel::Item* ObjectOverviewTreeModel::GetMaterialItem(ceNode *node)
{
  Item* item = FindItem (_root, node);
  if (!item)
    {
      return 0;
    }

  return FindItem (item, Item::Type_Material);
}


QModelIndex ObjectOverviewTreeModel::GetIndex(ceNode *node)
{
  Item* item = FindItem(_root, node);
  if (!item || item == _root)
    {
      return QModelIndex ();
    }

  int column = 0;
  int row = item->Parent->IndexOf(item);
  if (row < 0 || row >= item->Parent->Children.size())
    {
      return QModelIndex();
    }

  return createIndex(row, column, item);
}

void ObjectOverviewTreeModel::Clear(Item* item)
{
  if (item == 0)
    {
      if (_root)
        {
          Clear (_root);
        }
      _root = new Item;
      _root->type = Item::Type_Root;
      _root->Node = 0;
      _root->Parent = 0;
      return;
    }

  foreach (Item* itm, item->Children)
    {
      Clear(itm);
    }
  item->Children.clear();
  delete item;
}

void ObjectOverviewTreeModel::Build(ceSector* sector)
{
  Clear ();

  NodeListIterator it;
  for (it = sector->GetNodes().begin(); it != sector->GetNodes().end(); ++it)
    {
      AddNode (_root, *it);
    }
  reset();
}

void ObjectOverviewTreeModel::AddNode(Item* parent, ceNode *node)
{
  if (!node)
    {
      return;
    }

  Item* item = new Item ();
  item->Parent = parent;
  item->Node = node;
  parent->Children.append(item);

  bool composition = false;
  EditorSceneNode* esn = EditorSceneNode::FromNode(node);
  if (esn)
    {
      composition = esn->IsComposition();
    }


  switch (node->GetType())
    {
    case NT_Node:
      break;

    case NT_GroupNode:
      do
        {
          item->groupNode = static_cast<ceGroupNode*>(node);
          item->type = Item::Type_GroupNode;
          if (!composition || _showCompositions)
            {
              for (NodeListIterator it = item->groupNode->GetChildren().begin();
                   it != item->groupNode->GetChildren().end();
                   ++it)
                {
                  AddNode(item, *it);
                }
            }
        }
      while (false);
      break;

    case NT_EntityNode:
      do
        {
          item->entityNode = static_cast<ceEntityNode*>(node);
          item->type = Item::Type_EntityNode;

          if (!composition || _showCompositions)
            {
              AddGeometry(item, item->entityNode->GetGeometry());
              AddLight(item, item->entityNode->GetLight());
            }
        }
      while (false);
      break;

    case NT_LODNode:
      do
        {
          item->lodNode = static_cast<ceLODNode*>(node);
          item->type = Item::Type_LODNode;
          if (!composition || _showCompositions)
            {
              ceLevelOfDetail<ceNode>& lod = item->lodNode->Get();
              for (unsigned i=0, j=lod.GetNumberOfLevels(); i<j; ++i)
                {
                  AddNode (item, lod.Get(i));
                }
            }
        }
      while (false);
      break;

    default:
      break;

    }

}

void ObjectOverviewTreeModel::AddGeometry(Item *parent, ceGeometry *geometry)
{
  if (!geometry) return;

  Item* item = new Item();
  item->Parent = parent;
  if (parent)
    {
      parent->Children.append(item);
    }

  item->type = Item::Type_Geometry;
  item->Node = 0;
  item->geometry = geometry;

  ceMaterial* mat = geometry->GetMaterial();

  MaterialManager *mgr = Session::Get()->GetMaterialManager();
  AddMaterial(item, mgr->FindMaterial(mat));
}

void ObjectOverviewTreeModel::AddLight(Item *parent, ceLightObject *light)
{
  if (!light) return;
  Item* item = new Item();
  item->Parent = parent;
  if (parent)
    {
      parent->Children.append(item);
    }

  item->type = Item::Type_Light;
  item->Node = 0;
  item->light = light;
}

void ObjectOverviewTreeModel::AddMaterial(Item *parent, Material *material)
{
  Item* item = new Item();
  item->Parent = parent;
  if (parent)
    {
      parent->Children.append(item);
    }

  item->type = Item::Type_Material;
  item->Node = 0;
  item->material = material;
}

void ObjectOverviewTreeModel::AddMesh(Item *parent, iMesh *mesh)
{
  Item* item = new Item();
  item->Parent = parent;
  if (parent)
    {
      parent->Children.append(item);
    }

  item->type = Item::Type_Mesh;
  item->Node = 0;
  item->mesh = mesh;
}


int ObjectOverviewTreeModel::columnCount(const QModelIndex &parent) const
{
  return 2;
}

QVariant ObjectOverviewTreeModel::data(const QModelIndex &index, int role) const
{
  if (!index.isValid()) return QVariant ();
  if (role != Qt::DisplayRole) return QVariant ();

  Item* item = static_cast<Item*>(index.internalPointer());
  if (!item) return QVariant ();

  if (index.column() == 0)
    {
      switch (item->type)
        {
        case Item::Type_Root:
          return QVariant ("Root");

        case Item::Type_EntityNode:
          return QVariant ("ceEntityNode");

        case Item::Type_Geometry:
          return QVariant ("ceGeometry");

        case Item::Type_GroupNode:
          return QVariant ("ceGroupNode");

        case Item::Type_Light:
          return QVariant ("ceLightObject");

        case Item::Type_LODNode:
          return QVariant ("ceLODNode");

        case Item::Type_Material:
          return QVariant ("ceMaterial");

        case Item::Type_Mesh:
          return QVariant ("iMesh");

      }
    }
  else if (index.column() == 1)
    {
      switch (item->type)
        {
        case Item::Type_Root:
          return QVariant ("<root>");

        case Item::Type_EntityNode:
        case Item::Type_GroupNode:
        case Item::Type_LODNode:
          if (item->Node->GetName().size() == 0)
            {
              return QVariant ("<unnamed>");
            }
          return QVariant (item->Node->GetName().c_str());

        case Item::Type_Light:
          return QVariant ("<light>");

        case Item::Type_Geometry:
          return QVariant ("<geometry>");

        case Item::Type_Material:
          if (item->material)
            {
              return QVariant (item->material->GetCategory().GetFullName() + "." + item->material->GetExportName());
            }
          return QVariant ("<material>");

        case Item::Type_Mesh:
          return QVariant ("<mesh>");

        }

    }
  return QVariant ();
}

QVariant ObjectOverviewTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  if (role != Qt::DisplayRole) return QVariant ();
  switch (section)
    {
    case 0:
      return QVariant ("Type");

    case 1:
      return QVariant ("Name");

    }

  return QVariant ();
}

QModelIndex ObjectOverviewTreeModel::index(int row, int column, const QModelIndex &parent) const
{
  Item* parentItem = _root;
  if (parent.isValid())
    {
      parentItem = static_cast<Item*>(parent.internalPointer());
    }

  if (row < 0 || row >= parentItem->Children.size())
    {
      return QModelIndex();
    }

  return createIndex(row, column, parentItem->Children[row]);
}

QModelIndex ObjectOverviewTreeModel::parent(const QModelIndex &index) const
{
  if (!index.isValid()) return QModelIndex();

  Item* item = static_cast<Item*>(index.internalPointer());
  if (!item || item == _root)
    {
      return QModelIndex();
    }

  Item* parentItem = item->Parent;
  if (!parentItem || parentItem == _root)
    {
      return QModelIndex();
    }

  int row = 0;
  Item* parentParentItem = parentItem->Parent;
  if (parentParentItem)
    {
      row = parentParentItem->Children.indexOf(parentItem);
    }

  return createIndex(row, 0, parentItem);
}

int ObjectOverviewTreeModel::rowCount(const QModelIndex &parent) const
{
  if (!parent.isValid()) return _root->Children.size();

  Item* item = static_cast<Item*>(parent.internalPointer());
  if (!item) return 0;

  return item->Children.size();
}


